//
// Created by feng on 19-3-28.
//
#include "RandomGenerator.cuh"
#include <algorithm/rayTracing/qmcrt_util/global_constant.h>
#include <algorithm/rayTracing/qmcrt_util/global_function.cuh>

#include <ctime>
#include <random>

using namespace solar::cuda;
curandGenerator_t RandomGenerator::gen;

void RandomGenerator::initCudaRandGenerator()
{

    // printf("----------------initCudaRandGenerator--------------------------\n");
    // todo: generate a random num by Sth
    curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_DEFAULT);
    std::random_device rd;
    std::default_random_engine eng(rd());
    std::uniform_int_distribution<int> distr(0, INTMAX_MAX);
    //    long long ti = time(NULL);
    long long ti = distr(eng);
    curandSetPseudoRandomGeneratorSeed(gen, ti);
    // printf("Time:%lld\n", ti);
}

void RandomGenerator::destroyCudaRandGenerator()
{
    gen = nullptr;
    curandDestroyGenerator(gen);
}

__global__ static void mapFloat2int(int* d_intData, float const* d_floatData, int low_threshold,
                                    int high_threshold, size_t size)
{
    unsigned int myId = global_func::getThreadID();
    if (myId >= size)
    {
        return;
    }

    d_intData[myId] = int(d_floatData[myId] * (high_threshold - low_threshold) + low_threshold);
}

auto RandomGenerator::gpuUniform(int* d_min_max_array, int low_threshold, int high_threshold,
                                 int array_length, cudaStream_t stream) -> bool
{
    if (d_min_max_array == nullptr)
    {
        return false;
    }

    float* d_uniform = nullptr;
    checkCudaErrors(cudaMalloc((void**)&d_uniform, sizeof(float) * array_length));
    curandGenerateUniform(gen, d_uniform, array_length);

    int nThreads;
    dim3 nBlocks;
    if (!global_func::setThreadBlocks(nBlocks, nThreads, array_length))
    {
        return false;
    }

    mapFloat2int<<<nBlocks, nThreads, 0, stream>>>(d_min_max_array, d_uniform, low_threshold,
                                                   high_threshold, array_length);
    // checkCudaErrors(cudaDeviceSynchronize());
    checkCudaErrors(cudaGetLastError());
    checkCudaErrors(cudaFreeAsync(d_uniform, stream));
    return true;
}
